/* SPDX-License-Identifier: GPL-2.0-or-later */
/*******************************************************************************
 * Allwinner SoCs hdmi2.0 driver.
 *
 * Copyright (C) 2016 Allwinner.
 *
 * This file is licensed under the terms of the GNU General Public
 * License version 2.  This program is licensed "as is" without any
 * warranty of any kind, whether express or implied.
 ******************************************************************************/
#include <linux/delay.h>

#include "dw_dev.h"
#include "dw_phy.h"
#include "dw_avp.h"
#include "dw_mc.h"
#include "phy_top.h"
#include "phy_snps.h"

#define PHY_DRIVE_PARAMS_NUM		(5)

struct snps_phy_drive {
	u32 min_clk;  /* min tmds clock */
	u32 max_clk;  /* max tmds clock */
	u32 drive_data1;    /* txterm data */
	u32 drive_data2;    /* vlevctrl data */
	u32 drive_data3;    /* cksymtxctrl data */
};

struct snps_phy_mpll {
	u32 pixel_clk;
	/* 0 - 8bits; 1 - 10bits; 2 - 12bits; 3 - 16bits*/
	struct {
		u16 data1; /* opmode pll cfg */
		u16 data2; /* pll curr gmp ctrl */
		u16 data3; /* pll div ctrl */
	} mpll[4] ;
};

struct snps_phy_plat_s {
	u8 plat_id;
	u8 mpll_reg1;
	u8 mpll_reg2;
	u8 mpll_reg3;

	u8 drive_reg1;
	u8 drive_reg2;
	u8 drive_reg3;

	struct snps_phy_mpll  *mpll_rep0_table;
	struct snps_phy_mpll  *mpll_rep1_table;

	u32 mpll_rep0_size;
	u32 mpll_rep1_size;

	struct snps_phy_drive *drive_table;
	u32 drive_size;
};

static struct snps_phy_drive sun50i_drive[] = {
	{ 25175, 165000, 0x0004, 0x0232, 0x8009},
	{165000, 340000, 0x0005, 0x0210, 0x803d},
	{340000, 600000, 0x0000, 0x008A, 0x8029}
};

static struct snps_phy_drive sun60i_drive[] = {
	{ 25000, 165000, 0x0007, 0x8160, 0x8188},
	{165000, 340000, 0x0004, 0x8040, 0x8E85},
	{340000, 600000, 0x0000, 0x80c0, 0x82F6}
};

/**
 * snps phy mpll params need refer from databook.
 * sun50i: refer from product code: C082-0
 * sun60i: refer from product code: C422-0
 */

static struct snps_phy_mpll sun50i_mpll_rep0[] = {
	{ 25175, {
		{0x00B3, 0x0012, 0x0000}, {0x2153, 0x0012, 0x0000},
		{0x40F3, 0x0012, 0x0000}, {0x60B2, 0x0032, 0x0001}}},
	{ 27000, {
		{0x00B3, 0x0012, 0x0000}, {0x2153, 0x0012, 0x0000},
		{0x40F3, 0x0012, 0x0000}, {0x60B2, 0x0032, 0x0001}}},
	{ 31500, {
		{0x00B3, 0x0012, 0x0000}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{ 33750, {
		{0x00B3, 0x0012, 0x0000}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{ 35500, {
		{0x00B3, 0x0012, 0x0000}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{ 36000, {
		{0x00B3, 0x0012, 0x0000}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{ 40000, {
		{0x00B3, 0x0012, 0x0000}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{ 44900, {
		{0x00B3, 0x0012, 0x0000}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{ 49500, {
		{0x0072, 0x0013, 0x0001}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{ 50000, {
		{0x0072, 0x0013, 0x0001}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{ 50350, {
		{0x0072, 0x0013, 0x0001}, {0x2142, 0x001E, 0x0001},
		{0x40A2, 0x0027, 0x0001}, {0x6071, 0x003A, 0x0002}}},
	{ 54000, {
		{0x0072, 0x0013, 0x0001}, {0x2142, 0x001E, 0x0001},
		{0x40A2, 0x0027, 0x0001}, {0x6071, 0x003A, 0x0002}}},
	{ 56250, {
		{0x0072, 0x0013, 0x0001}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{ 59400, {
		{0x0072, 0x0013, 0x0001}, {0x2142, 0x001E, 0x0001},
		{0x40A2, 0x0027, 0x0001}, {0x6071, 0x003A, 0x0002}}},
	{ 65000, {
		{0x0072, 0x0013, 0x0001}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{ 68250, {
		{0x0072, 0x0013, 0x0001}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{ 71000, {
		{0x0072, 0x0013, 0x0001}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{ 72000, {
		{0x0072, 0x0013, 0x0001}, {0x2142, 0x001E, 0x0001},
		{0x4061, 0x002A, 0x0002}, {0x6071, 0x003A, 0x0002}}},
	{ 73250, {
		{0x0072, 0x0013, 0x0001}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{ 74250, {
		{0x0072, 0x0013, 0x0001}, {0x2145, 0x003F, 0x0002},
		{0x4061, 0x002A, 0x0002}, {0x6071, 0x003A, 0x0002}}},
	{ 75000, {
		{0x0072, 0x0013, 0x0001}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{ 78750, {
		{0x0072, 0x0013, 0x0001}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{ 79500, {
		{0x0072, 0x0013, 0x0001}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{ 82500, {
		{0x0072, 0x0013, 0x0001}, {0x2145, 0x003F, 0x0002},
		{0x4061, 0x002A, 0x0002}, {0x6071, 0x003A, 0x0002}}},
	{ 83500, {
		{0x0072, 0x0013, 0x0001}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{ 85500, {
		{0x0072, 0x0013, 0x0001}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{ 88750, {
		{0x0072, 0x0013, 0x0001}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{ 90000, {
		{0x0072, 0x0013, 0x0001}, {0x2145, 0x003F, 0x0002},
		{0x4061, 0x002A, 0x0002}, {0x6071, 0x003A, 0x0002}}},
	{ 94500, {
		{0x0051, 0x0019, 0x0002}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{ 99000, {
		{0x0051, 0x0019, 0x0002}, {0x2145, 0x003F, 0x0002},
		{0x4061, 0x002A, 0x0002}, {0x6050, 0x003D, 0x0003}}},
	{100700, {
		{0x0051, 0x0019, 0x0002}, {0x2145, 0x003F, 0x0002},
		{0x4061, 0x002A, 0x0002}, {0x6050, 0x003D, 0x0003}}},
	{101000, {
		{0x0051, 0x0019, 0x0002}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{102250, {
		{0x0051, 0x0019, 0x0002}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{106500, {
		{0x0051, 0x0019, 0x0002}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{108000, {
		{0x0051, 0x0019, 0x0002}, {0x2145, 0x003F, 0x0002},
		{0x4061, 0x002A, 0x0002}, {0x6050, 0x003D, 0x0003}}},
	{115500, {
		{0x0051, 0x0019, 0x0002}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{117500, {
		{0x0051, 0x0019, 0x0002}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{118800, {
		{0x0051, 0x0019, 0x0002}, {0x2145, 0x003F, 0x0002},
		{0x4061, 0x002A, 0x0002}, {0x6050, 0x003D, 0x0003}}},
	{119000, {
		{0x0051, 0x0019, 0x0002}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{121750, {
		{0x0051, 0x0019, 0x0002}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{122500, {
		{0x0051, 0x0019, 0x0002}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{135000, {
		{0x0051, 0x0019, 0x0002}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{136750, {
		{0x0051, 0x0019, 0x0002}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{140250, {
		{0x0051, 0x0019, 0x0002}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{144000, {
		{0x0051, 0x0019, 0x0002}, {0x2145, 0x003F, 0x0002},
		{0x4064, 0x0024, 0x0002}, {0x6050, 0x003D, 0x0003}}},
	{146250, {
		{0x0051, 0x0019, 0x0002}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{148250, {
		{0x0051, 0x0019, 0x0002}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{148500, {
		{0x0051, 0x0019, 0x0002}, {0x214C, 0x001B, 0x0001},
		{0x4064, 0x0024, 0x0002}, {0x6050, 0x003D, 0x0003}}},
	{154000, {
		{0x0051, 0x0019, 0x0002}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{156000, {
		{0x0051, 0x0019, 0x0002}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{157000, {
		{0x0051, 0x0019, 0x0002}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{157500, {
		{0x0051, 0x0019, 0x0002}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{162000, {
		{0x0051, 0x0019, 0x0002}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{165000, {
		{0x0051, 0x0019, 0x0002}, {0x214C, 0x001B, 0x0001},
		{0x4064, 0x0024, 0x0002}, {0x6050, 0x003D, 0x0003}}},
	{175500, {
		{0x0051, 0x0019, 0x0002}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{179500, {
		{0x0051, 0x0019, 0x0002}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{180000, {
		{0x0051, 0x0019, 0x0002}, {0x214C, 0x001B, 0x0001},
		{0x4064, 0x0024, 0x0002}, {0x7A50, 0x0030, 0x0003}}},
	{182750, {
		{0x0051, 0x0019, 0x0002}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{185625, {
		{0x0040, 0x0019, 0x0003}, {0x214C, 0x001B, 0x0001},
		{0x4064, 0x0024, 0x0002}, {0x7A50, 0x0030, 0x0003}}},
	{187000, {
		{0x0040, 0x0019, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{187250, {
		{0x0040, 0x0019, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{189000, {
		{0x0040, 0x0019, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{193250, {
		{0x0040, 0x0019, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{198000, {
		{0x0040, 0x0019, 0x0003}, {0x214C, 0x001B, 0x0001},
		{0x4064, 0x0024, 0x0002}, {0x7A50, 0x0030, 0x0003}}},
	{202500, {
		{0x0040, 0x0019, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{204750, {
		{0x0040, 0x0019, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{208000, {
		{0x0040, 0x0019, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{214750, {
		{0x0040, 0x0019, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{216000, {
		{0x0040, 0x0019, 0x0003}, {0x214C, 0x001B, 0x0001},
		{0x4064, 0x0024, 0x0002}, {0x7A50, 0x0030, 0x0003}}},
	{218250, {
		{0x0040, 0x0019, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{229500, {
		{0x0040, 0x0019, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{234000, {
		{0x0040, 0x0019, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{237600, {
		{0x0040, 0x0019, 0x0003}, {0x214C, 0x001B, 0x0001},
		{0x5A64, 0x003F, 0x0003}, {0x7A50, 0x0030, 0x0003}}},
	{245250, {
		{0x0040, 0x0019, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{245500, {
		{0x0040, 0x0019, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{261000, {
		{0x0040, 0x0019, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{268250, {
		{0x0040, 0x0019, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{268500, {
		{0x0040, 0x0019, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{281250, {
		{0x0040, 0x0019, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{288000, {
		{0x0040, 0x0036, 0x0003}, {0x3B4C, 0x003D, 0x0003},
		{0x5A64, 0x003F, 0x0003}, {0x7A50, 0x0022, 0x0003}}},
	{297000, {
		{0x0040, 0x0019, 0x0003}, {0x3B4C, 0x003D, 0x0003},
		{0x5A64, 0x003F, 0x0003}, {0x7A50, 0x0022, 0x0003}}},
	{317000, {
		{0x0040, 0x0019, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{330000, {
		{0x0040, 0x0019, 0x0003}, {0x3B4C, 0x003D, 0x0003},
		{0x5A64, 0x003F, 0x0003}, {0x0000, 0x0000, 0x0000}}},
	{333250, {
		{0x0040, 0x0019, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{340000, {
		{0x0040, 0x0019, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{348500, {
		{0x1A40, 0x0010, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{356500, {
		{0x1A40, 0x0010, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{360000, {
		{0x1A40, 0x0010, 0x0003}, {0x3B4C, 0x003D, 0x0003},
		{0x5A64, 0x003F, 0x0003}, {0x0000, 0x0000, 0x0000}}},
	{371250, {
		{0x1A40, 0x0010, 0x0003}, {0x3B4C, 0x003D, 0x0003},
		{0x5A64, 0x003F, 0x0003}, {0x0000, 0x0000, 0x0000}}},
	{380500, {
		{0x1A40, 0x0010, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{396000, {
		{0x1A40, 0x0010, 0x0003}, {0x3B4C, 0x003D, 0x0003},
		{0x5A64, 0x003F, 0x0003}, {0x0000, 0x0000, 0x0000}}},
	{432000, {
		{0x1A40, 0x0010, 0x0003}, {0x3B4C, 0x003D, 0x0003},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{443250, {
		{0x1A40, 0x0010, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{475200, {
		{0x1A40, 0x0010, 0x0003}, {0x3B4C, 0x003D, 0x0003},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{495000, {
		{0x1A40, 0x0010, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{505250, {
		{0x1A40, 0x0010, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{552750, {
		{0x1A40, 0x0010, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{594000, {
		{0x1A7c, 0x0010, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
};

static struct snps_phy_mpll sun50i_mpll_rep1[] = {
	{ 13500, {
		{0x0133, 0x0018, 0x0000}, {0x2173, 0x0010, 0x0000},
		{0x41B3, 0x0018, 0x0000}, {0x6132, 0x003A, 0x0001}}},
	{ 27000, {
		{0x00B2, 0x0032, 0x0001}, {0x2152, 0x0032, 0x0001},
		{0x40F2, 0x0032, 0x0001}, {0x60B1, 0x003F, 0x0002}}},
	{ 54000, {
		{0x0071, 0x003A, 0x0002}, {0x2141, 0x003F, 0x0002},
		{0x40A1, 0x003F, 0x0002}, {0x6070, 0x0013, 0x0001}}},
	{108000, {
		{0x0050, 0x003D, 0x0003}, {0x215C, 0x0012, 0x0000},
		{0x4060, 0x0024, 0x0002}, {0x7A70, 0x003F, 0x0003}}},
	{297000, {
		{0x1A50, 0x0022, 0x0003}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
};

static struct snps_phy_mpll sun60i_mpll_rep0[] = {
	{ 25175, {
		{0x0003, 0x0283, 0x0628}, {0x1003, 0x0281, 0x0632},
		{0x2003, 0x02C2, 0x023C}, {0x3002, 0x1283, 0x0628}}},
	{ 27000, {
		{0x0003, 0x0283, 0x0628}, {0x1003, 0x0281, 0x0632},
		{0x2003, 0x02C2, 0x023C}, {0x3002, 0x1283, 0x0628}}},
	{ 33750, {
		{0x0003, 0x0283, 0x0628}, {0x0003, 0x0283, 0x0628},
		{0x0003, 0x0283, 0x0628}, {0x0003, 0x0283, 0x0628}}},
	{ 35500, {
		{0x0003, 0x0283, 0x0628}, {0x0003, 0x0283, 0x0628},
		{0x0003, 0x0283, 0x0628}, {0x0003, 0x0283, 0x0628}}},
	{ 36000, {
		{0x0003, 0x0285, 0x0228}, {0x0003, 0x0285, 0x0228},
		{0x0003, 0x0285, 0x0228}, {0x0003, 0x0285, 0x0228}}},
	{ 40000, {
		{0x0003, 0x0285, 0x0228}, {0x0003, 0x0285, 0x0228},
		{0x0003, 0x0285, 0x0228}, {0x0003, 0x0285, 0x0228}}},
	{ 44900, {
		{0x0003, 0x0285, 0x0228}, {0x0003, 0x0285, 0x0228},
		{0x0003, 0x0285, 0x0228}, {0x0003, 0x0285, 0x0228}}},
	{ 49500, {
		{0x0002, 0x1183, 0x0614}, {0x0002, 0x1183, 0x0614},
		{0x0002, 0x1183, 0x0614}, {0x0002, 0x1183, 0x0614}}},
	{ 50000, {
		{0x0002, 0x1183, 0x0614}, {0x0002, 0x1183, 0x0614},
		{0x0002, 0x1183, 0x0614}, {0x0002, 0x1183, 0x0614}}},
	{ 50350, {
		{0x0002, 0x1183, 0x0614}, {0x1002, 0x1203, 0x0619},
		{0x2002, 0x1202, 0x021E}, {0x3001, 0x2183, 0x0614}}},
	{ 54000, {
		{0x0002, 0x1183, 0x0694}, {0x1002, 0x0203, 0x0619},
		{0x2002, 0x1202, 0x021E}, {0x3001, 0x2183, 0x0614}}},
	{ 56250, {
		{0x0002, 0x1183, 0x0614}, {0x0002, 0x1183, 0x0614},
		{0x0002, 0x1183, 0x0614}, {0x0002, 0x1183, 0x0614}}},
	{ 59400, {
		{0x0002, 0x1183, 0x0614}, {0x1002, 0x1182, 0x0C14},
		{0x2002, 0x1202, 0x021E}, {0x3001, 0x2183, 0x0614}}},
	{ 65000, {
		{0x0002, 0x1183, 0x0614}, {0x0002, 0x1183, 0x0614},
		{0x0002, 0x1183, 0x0614}, {0x0002, 0x1183, 0x0614}}},
	{ 68250, {
		{0x0002, 0x1183, 0x0614}, {0x0002, 0x1183, 0x0614},
		{0x0002, 0x1183, 0x0614}, {0x0002, 0x1183, 0x0614}}},
	{ 71000, {
		{0x0002, 0x1183, 0x0614}, {0x0002, 0x1183, 0x0614},
		{0x0002, 0x1183, 0x0614}, {0x0002, 0x1183, 0x0614}}},
	{ 72000, {
		{0x0002, 0x1142, 0x0214}, {0x1002, 0x1182, 0x0219},
		{0x2001, 0x2141, 0x060F}, {0x3001, 0x2142, 0x0214}}},
	{ 73250, {
		{0x0002, 0x1142, 0x0214}, {0x0002, 0x1142, 0x0214},
		{0x0002, 0x1142, 0x0214}, {0x0002, 0x1142, 0x0214}}},
	{ 74250, {
		{0x0002, 0x1142, 0x0214}, {0x1009, 0x2203, 0x0619},
		{0x2001, 0x2141, 0x060F}, {0x3001, 0x2142, 0x0214}}},
	{ 82500, {
		{0x0002, 0x1142, 0x0214}, {0x1009, 0x2203, 0x0619},
		{0x2001, 0x2141, 0x060F}, {0x3001, 0x2142, 0x0214}}},
	{ 90000, {
		{0x0002, 0x1142, 0x0214}, {0x1009, 0x2203, 0x0619},
		{0x2001, 0x2141, 0x060F}, {0x3001, 0x2142, 0x0214}}},
	{ 99000, {
		{0x0001, 0x20C0, 0x060A}, {0x1009, 0x2203, 0x0619},
		{0x2001, 0x2100, 0x020F}, {0x3000, 0x30C0, 0x060A}}},
	{100700, {
		{0x0001, 0x20C0, 0x060A}, {0x1009, 0x2203, 0x0619},
		{0x2001, 0x2100, 0x020F}, {0x3000, 0x30C0, 0x060A}}},
	{108000, {
		{0x0001, 0x20C0, 0x060A}, {0x1009, 0x2203, 0x0619},
		{0x2001, 0x2100, 0x020F}, {0x3000, 0x30C0, 0x060A}}},
	{118000, {
		{0x0001, 0x20C0, 0x060A}, {0x1009, 0x2182, 0x0219},
		{0x2001, 0x2100, 0x020F}, {0x3000, 0x30C0, 0x060A}}},
	{144000, {
		{0x0001, 0x2080, 0x020A}, {0x1009, 0x2182, 0x0219},
		{0x2008, 0x3141, 0x060F}, {0x3000, 0x3080, 0x020A}}},
	{148500, {
		{0x0001, 0x2080, 0x020A}, {0x1018, 0x3203, 0x0619},
		{0x2008, 0x3141, 0x060F}, {0x3000, 0x3080, 0x020A}}},
	{165000, {
		{0x0001, 0x2080, 0x020A}, {0x1018, 0x3203, 0x0619},
		{0x2008, 0x3141, 0x060F}, {0x3000, 0x3080, 0x020A}}},
	{180000, {
		{0x0001, 0x2080, 0x020A}, {0x1018, 0x3203, 0x0619},
		{0x2008, 0x3141, 0x060F}, {0x3640, 0x3080, 0x020A}}},
	{185625, {
		{0x0000, 0x3040, 0x0605}, {0x1018, 0x3203, 0x0619},
		{0x2008, 0x3141, 0x060F}, {0x3640, 0x3080, 0x020A}}},
	{198000, {
		{0x0000, 0x3040, 0x0605}, {0x1018, 0x3203, 0x0619},
		{0x2008, 0x3100, 0x020F}, {0x3640, 0x3080, 0x020A}}},
	{216000, {
		{0x0000, 0x3040, 0x0605}, {0x1018, 0x3203, 0x0619},
		{0x2008, 0x3100, 0x020F}, {0x3640, 0x3080, 0x020A}}},
	{237600, {
		{0x0000, 0x3040, 0x0605}, {0x1018, 0x3182, 0x0219},
		{0x2648, 0x3100, 0x020F}, {0x3640, 0x30C0, 0x000A}}},
	{288000, {
		{0x0000, 0x3041, 0x0205}, {0x1658, 0x3182, 0x0219},
		{0x2648, 0x3100, 0x020F}, {0x3640, 0x30C0, 0x000A}}},
	{297000, {
		{0x0000, 0x3041, 0x0205}, {0x1658, 0x3182, 0x0219},
		{0x2648, 0x3100, 0x020F}, {0x3640, 0x30C0, 0x000A}}},
	{330000, {
		{0x0000, 0x3041, 0x0205}, {0x1658, 0x3182, 0x0219},
		{0x2648, 0x3100, 0x000F}, {0x0000, 0x0000, 0x0000}}},
	{360000, {
		{0x0640, 0x3041, 0x0205}, {0x1658, 0x3182, 0x0219},
		{0x2648, 0x3100, 0x000F}, {0x0000, 0x0000, 0x0000}}},
	{371250, {
		{0x0640, 0x3041, 0x0205}, {0x1658, 0x3182, 0x0219},
		{0x2648, 0x3100, 0x000F}, {0x0000, 0x0000, 0x0000}}},
	{396000, {
		{0x0640, 0x3041, 0x0205}, {0x1658, 0x31C0, 0x0019},
		{0x2648, 0x3100, 0x000F}, {0x0000, 0x0000, 0x0000}}},
	{432000, {
		{0x0640, 0x3041, 0x0205}, {0x1658, 0x31C0, 0x0019},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{495000, {
		{0x0640, 0x3080, 0x0005}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{505250, {
		{0x0640, 0x3080, 0x0005}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{552750, {
		{0x0640, 0x3080, 0x0005}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
	{594000, {
		{0x0640, 0x3080, 0x0005}, {0x0000, 0x0000, 0x0000},
		{0x0000, 0x0000, 0x0000}, {0x0000, 0x0000, 0x0000}}},
};

static struct snps_phy_mpll sun60i_mpll_rep1[] = {
	{ 13500, {
		{0x0003, 0x0280, 0x0650}, {0x1003, 0x0280, 0x0664},
		{0x2003, 0x02C0, 0x0278}, {0x3002, 0x1280, 0x0650}}},
	{ 27000, {
		{0x0002, 0x1283, 0x0628}, {0x1002, 0x1281, 0x0632},
		{0x2002, 0x12C2, 0x023C}, {0x3001, 0x2283, 0x0628}}},
	{ 54000, {
		{0x0001, 0x2183, 0x0614}, {0x1001, 0x2203, 0x0619},
		{0x2001, 0x2202, 0x021E}, {0x3000, 0x3183, 0x0614}}},
	{108000, {
		{0x0000, 0x30C0, 0x060A}, {0x1018, 0x3281, 0x0632},
		{0x2000, 0x3100, 0x020F}, {0x3640, 0x3142, 0x0214}}},
};

static struct snps_phy_plat_s sun50i_phy = {
	.plat_id = HDMI_SUN50I_W9_P1,
	.mpll_reg1       = 0x06,
	.mpll_reg2       = 0x10,
	.mpll_reg3       = 0x15,
	.drive_reg1      = 0x19,
	.drive_reg2      = 0x0E,
	.drive_reg3      = 0x09,

	.mpll_rep0_table = sun50i_mpll_rep0,
	.mpll_rep0_size  = ARRAY_SIZE(sun50i_mpll_rep0),

	.mpll_rep1_table = sun50i_mpll_rep1,
	.mpll_rep1_size  = ARRAY_SIZE(sun50i_mpll_rep1),

	.drive_table     = sun50i_drive,
	.drive_size      = ARRAY_SIZE(sun50i_drive),
};

static struct snps_phy_plat_s sun60i_phy = {
	.plat_id = HDMI_SUN60I_W2_P1,
	.mpll_reg1       = 0x06,
	.mpll_reg2       = 0x10,
	.mpll_reg3       = 0x11,
	.drive_reg1      = 0x19,
	.drive_reg2      = 0x0E,
	.drive_reg3      = 0x09,

	.mpll_rep0_table = sun60i_mpll_rep0,
	.mpll_rep0_size  = ARRAY_SIZE(sun60i_mpll_rep0),

	.mpll_rep1_table = sun60i_mpll_rep1,
	.mpll_rep1_size  = ARRAY_SIZE(sun60i_mpll_rep1),

	.drive_table     = sun60i_drive,
	.drive_size      = ARRAY_SIZE(sun60i_drive),
};

static struct snps_phy_plat_s *sunxi_plat[] = {
	&sun50i_phy,
	&sun60i_phy,
};

static struct snps_phy_plat_s    *snps_phy;

/**
 * @desc: get mpll params indec from color depth
 * @return: 0 - 8bits params
 *          1 - 10bits params
 *          2 - 12bits params
 *          3 - 16bits params
 */
static u8 _bits_to_index(u8 bits)
{
	if (bits == DW_COLOR_DEPTH_10)
		return 1;
	else if (bits == DW_COLOR_DEPTH_12)
		return 2;
	else if (bits == DW_COLOR_DEPTH_16)
		return 3;
	else
		return 0;
}

static int _snps_phy_config_init(void)
{
	/* power down flow */
	dw_mc_sw_reset(DW_MC_SWRST_PHY, 0x1);
	dw_phy_set_power(DW_HDMI_DISABLE);

	/* power up flow */
	dw_phy_config_svsret();
	dw_mc_sw_reset(DW_MC_SWRST_PHY, 0x0);
	dw_phy_config_interface();
	return 0;
}

static int _snps_phy_cfg_mpll(void)
{
	struct snps_phy_mpll *table = NULL;
	struct dw_hdmi_dev_s *hdmi = dw_get_hdmi();
	u32 ref_clk = 0, clk = hdmi->pixel_clk, size = 0, i = 0;
	u8 index = _bits_to_index(hdmi->color_bits);

	switch (hdmi->pixel_repeat) {
	case 0x1:
		table = snps_phy->mpll_rep1_table;
		size  = snps_phy->mpll_rep1_size;
		break;
	case 0x0:
	default:
		table = snps_phy->mpll_rep0_table;
		size  = snps_phy->mpll_rep0_size;
		break;
	}

	/* check min clock */
	if (clk < table[0].pixel_clk) {
		ref_clk = table[0].pixel_clk;
		hdmi_wrn("snps phy check clk: %dHz is too low and change use: %dHz\n",
			clk, ref_clk);
		goto clk_cfg;
	}

	/* check max clock */
	if (clk > table[size - 1].pixel_clk) {
		ref_clk = table[size - 1].pixel_clk;
		hdmi_wrn("snps phy check clk: %dHz is too max and change use: %dHz\n",
			clk, ref_clk);
		goto clk_cfg;
	}

	/* check clock is match */
	for (i = 0; i < size; i++) {
		if (clk == table[i].pixel_clk) {
			ref_clk = clk;
			goto clk_cfg;
		}
	}

	/* check and use near clock */
	for (i = 0; i < (size - 1); i++) {
		if (clk < table[i].pixel_clk)
			continue;
		if (clk > table[i + 1].pixel_clk)
			continue;
		if ((clk - table[i].pixel_clk) > (table[i + 1].pixel_clk - clk))
			ref_clk = table[i + 1].pixel_clk;
		else
			ref_clk = table[i].pixel_clk;
		hdmi_wrn("snps phy check clk: %dHz is match in [%dHz-%dHz] and change use: %dHz\n",
			clk, table[i].pixel_clk, table[i + 1].pixel_clk, ref_clk);
		goto clk_cfg;
	}

clk_cfg:
	for (i = 0; i < size; i++) {
		if (ref_clk != table[i].pixel_clk)
			continue;

		dw_phy_write(snps_phy->mpll_reg1, table[i].mpll[index].data1);
		dw_phy_write(snps_phy->mpll_reg2, table[i].mpll[index].data2);
		dw_phy_write(snps_phy->mpll_reg3, table[i].mpll[index].data3);

		hdmi_inf("[snps phy]\n");
		hdmi_inf(" - refer clock: %dKHz, color bits: %d, repeat: %d\n",
				ref_clk, hdmi->color_bits, hdmi->pixel_repeat);
		hdmi_inf("    - mpll: 0x%04x, 0x%04x, 0x%04x\n",
				table[i].mpll[index].data1,
				table[i].mpll[index].data2,
				table[i].mpll[index].data3);
		return 0;
	}
	return -1;
}

static int _snps_phy_cfg_drive(void)
{
	struct dw_hdmi_dev_s  *hdmi  = dw_get_hdmi();
	struct snps_phy_drive *table = snps_phy->drive_table;
	u32 clock = hdmi->tmds_clk, i = 0;

	for (i = 0; i < snps_phy->drive_size; i++) {
		if (table[i].min_clk == table[i].max_clk) {
			if (clock != table[i].min_clk)
				continue;
		} else {
			if (clock < table[i].min_clk)
				continue;
			if (clock >= table[i].max_clk)
				continue;
		}

		hdmi_inf(" - refer clock: %dKHz. [%dKHz~%dKHz]\n",
				clock, table[i].min_clk, table[i].max_clk);
		hdmi_inf("    - drive: 0x%04x, 0x%04x, 0x%04x\n",
				table[i].drive_data1,
				table[i].drive_data2,
				table[i].drive_data3);

		dw_phy_write(snps_phy->drive_reg1, table[i].drive_data1);
		dw_phy_write(snps_phy->drive_reg2, table[i].drive_data2);
		dw_phy_write(snps_phy->drive_reg3, table[i].drive_data3);
		return 0;
	}

	hdmi_err("snps phy check clock %dHz unmatch in table!\n", clock);
	return -1;
}

int snps_phy_disconfig(void)
{
	int ret = 0;

	ret = dw_phy_standby();
	if (ret != 0) {
		hdmi_err("dw phy standby failed\n");
		return -1;
	}

	return ret;
}

int snps_phy_config(void)
{
	int ret = 0;

	_snps_phy_config_init();

	ret = _snps_phy_cfg_mpll();
	if (ret != 0) {
		hdmi_err("snps phy config mpll failed\n");
		goto failed_exit;
	}
	ret = _snps_phy_cfg_drive();
	if (ret != 0) {
		hdmi_err("snps phy config drive failed\n");
		goto failed_exit;
	}

	dw_phy_set_power(DW_HDMI_ENABLE);

	ret = dw_phy_wait_lock();
	hdmi_inf("snps phy state: %s\n", ret == 1 ? "lock" : "unlock");
	if (ret == 1)
		return 0;

failed_exit:
	return -1;
}

int snps_phy_write(u8 addr, void *data)
{
	u16 *value = (u16 *)data;

	if (IS_ERR_OR_NULL(value)) {
		shdmi_err(value);
		return -1;
	}

	return dw_phy_write(addr, *value);
}

int snps_phy_read(u8 addr, void *data)
{
	u16 *value = (u16 *)data;

	if (IS_ERR_OR_NULL(value)) {
		shdmi_err(value);
		return -1;
	}

	return dw_phy_read(addr, value);
}

ssize_t snps_phy_dump(char *buf)
{
	ssize_t n = 0;
	u16 mpll_data[3] = {0}, drive_data[3] = {0};

	snps_phy_read(snps_phy->mpll_reg1, &mpll_data[0]);
	snps_phy_read(snps_phy->mpll_reg2, &mpll_data[1]);
	snps_phy_read(snps_phy->mpll_reg3, &mpll_data[2]);

	snps_phy_read(snps_phy->drive_reg1, &drive_data[0]);
	snps_phy_read(snps_phy->drive_reg2, &drive_data[1]);
	snps_phy_read(snps_phy->drive_reg3, &drive_data[2]);

	n += sprintf(buf + n, "\n[snps phy]\n");
	n += sprintf(buf + n, " - mpll : 0x%04X, 0x%04X, 0x%04X\n",
			mpll_data[0], mpll_data[1], mpll_data[2]);
	n += sprintf(buf + n, " - drive: 0x%04X, 0x%04X, 0x%04X\n",
			drive_data[0], drive_data[1], drive_data[2]);
	return n;
}

static void _snps_phy_match_plat(u8 plat_id)
{
	int i = 0;

	for (i = 0; i < ARRAY_SIZE(sunxi_plat); i++) {
		if (sunxi_plat[i]->plat_id == plat_id) {
			snps_phy = sunxi_plat[i];
			return;
		}
	}
	hdmi_err("snps phy unsupport plat id: %d\n", plat_id);
	snps_phy = NULL;
}

int snps_phy_init(void)
{
	int ret = 0, i = 0;
	u32 *tmp_buf = NULL, tmp_size = 0;
	struct dw_hdmi_dev_s *hdmi = dw_get_hdmi();
	struct device_node *hdmi_node = hdmi->dev->of_node;

	_snps_phy_match_plat(hdmi->plat_id);

	/* get phy drive when dts is config */
	tmp_size = of_property_count_elems_of_size(hdmi_node, "snps_phy", sizeof(u32));
	if (tmp_size <= 0) {
		hdmi_inf("snps phy not get table from dts, use default\n");
		goto exit;
	}
	snps_phy->drive_size = tmp_size / PHY_DRIVE_PARAMS_NUM;

	tmp_buf = kmalloc(snps_phy->drive_size * sizeof(struct snps_phy_drive),
			GFP_KERNEL | __GFP_ZERO);
	if (!tmp_buf) {
		hdmi_err("snps phy alloc buffer failed\n");
		goto exit;
	}

	ret = of_property_read_u32_array(hdmi_node, "snps_phy", tmp_buf, tmp_size);
	if (ret < 0) {
		hdmi_err("snps phy get dts table value failed\n");
		goto exit;
	}

	snps_phy->drive_table = (struct snps_phy_drive *)tmp_buf;

	hdmi_trace("snps phy dts:\n");
	for (i = 0; i < snps_phy->drive_size; i++) {
		hdmi_trace(" - line[%d]: [%d ~ %d], 0x%04x, 0x%04x, 0x%04x\n", i,
			snps_phy->drive_table[i].min_clk,
			snps_phy->drive_table[i].max_clk,
			snps_phy->drive_table[i].drive_data1,
			snps_phy->drive_table[i].drive_data1,
			snps_phy->drive_table[i].drive_data1);
	}

exit:
	return 0;
}
